﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Etapa1.ModelDescription;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Etapa1
{    
    public class PnTriangles
    {
        Triangulo tri;
        private PnTriangles(Triangulo tri)
        {
            this.tri = tri;
            calculateControlPoint();
        }

        public static void PnTrianglesProcessor(List<Vertice> vertices, List<Edge> edges,List<Triangulo> triangles)
        {
            List<PnTriangles> tris = new List<PnTriangles>();
            foreach (var item in triangles)
            {
                PnTriangles p = new PnTriangles(item);
                tris.Add(p);
            }
            triangles.Clear();
            vertices.Clear();
            edges.Clear();

            foreach (var item in tris)
	        {
                item.AddGeneratedTriangles(vertices, edges, triangles);
	        } 
        }

        public void AddGeneratedTriangles(List<Vertice> vertices, List<Edge> edges,List<Triangulo> triangles)
        {
            
            Vector2 textCoord0 = tri.Vertices[0].VertexInfo.TextureCoordinate;
            Vector2 textCoord1 = tri.Vertices[1].VertexInfo.TextureCoordinate;
            Vector2 textCoord2 = tri.Vertices[2].VertexInfo.TextureCoordinate;
            Vector2 textCoord05 = (tri.Vertices[0].VertexInfo.TextureCoordinate + tri.Vertices[1].VertexInfo.TextureCoordinate) * 0.5f;
            Vector2 textCoord15 = (tri.Vertices[1].VertexInfo.TextureCoordinate + tri.Vertices[2].VertexInfo.TextureCoordinate) * 0.5f;
            Vector2 textCoord25 = (tri.Vertices[2].VertexInfo.TextureCoordinate + tri.Vertices[0].VertexInfo.TextureCoordinate) * 0.5f;

            Vector3 normal0 = NormOnParametricSurface(0, 0);
            normal0.Normalize();
            Vector3 normal1 = NormOnParametricSurface(1, 0);
            normal1.Normalize();
            Vector3 normal2 = NormOnParametricSurface(0, 1);
            normal2.Normalize();
            Vector3 normal05 = NormOnParametricSurface(0.5f, 0);
            normal05.Normalize();
            Vector3 normal15 = NormOnParametricSurface(0.5f, 0.5f);
            normal15.Normalize();
            Vector3 normal25 = NormOnParametricSurface(0, 0.5f);
            normal25.Normalize();


            Vector3 pos0 = PointOnParametricSurface(0, 0);
            Vector3 pos1 = PointOnParametricSurface(1, 0);
            Vector3 pos2 = PointOnParametricSurface(0, 1);
            Vector3 pos05 = PointOnParametricSurface(0.5f, 0);
            Vector3 pos15 = PointOnParametricSurface(0.5f, 0.5f);
            Vector3 pos25 = PointOnParametricSurface(0, 0.5f);
            
            Vertice v0 = new Vertice(new VertexPositionNormalTexture(pos0,normal0,textCoord0),vertices.Count);
            vertices.Add(v0);
            Vertice v1 = new Vertice(new VertexPositionNormalTexture(pos1, normal1, textCoord1), vertices.Count);
            vertices.Add(v1);
            Vertice v2 = new Vertice(new VertexPositionNormalTexture(pos2, normal2, textCoord2), vertices.Count);
            vertices.Add(v2);
            Vertice v05 = new Vertice(new VertexPositionNormalTexture(pos05, normal05, textCoord05), vertices.Count);
            vertices.Add(v05);
            Vertice v15 = new Vertice(new VertexPositionNormalTexture(pos15, normal15, textCoord15), vertices.Count);
            vertices.Add(v15);
            Vertice v25 = new Vertice(new VertexPositionNormalTexture(pos25, normal25, textCoord25), vertices.Count);
            vertices.Add(v25);

            triangles.Add(CreateTriangle(v0, v05, v25,edges));
            triangles.Add(CreateTriangle(v05, v1, v15,edges));
            triangles.Add(CreateTriangle(v25, v05, v15, edges));
            triangles.Add(CreateTriangle(v25, v15, v2, edges));
        }

        Triangulo CreateTriangle(Vertice v0, Vertice v1, Vertice v2, List<Edge> edgeList)
        {
            Triangulo newTriangulo = new Triangulo(v0, v1, v2);
            v0.addTriangulo(newTriangulo);
            v1.addTriangulo(newTriangulo);
            v2.addTriangulo(newTriangulo);

            Edge[] tempedges = new Edge[] { new Edge(v0, v1), new Edge(v1, v2), new Edge(v2, v0) };

            for (int j = 0; j < 3; j++)
            {
                ///LENTO Q DOI, BUT I DID NOT FIND AN EASY WAY yet
                Edge edx = edgeList.Find((a) => a.Vertices[0] == tempedges[j].Vertices[1] && a.Vertices[1] == tempedges[j].Vertices[0] || a.Vertices[0] == tempedges[j].Vertices[0] && a.Vertices[1] == tempedges[j].Vertices[1]);
                if (edx == null)
                {
                    tempedges[j].TrianguloPertencente.Add(newTriangulo);
                    tempedges[j].PositionInEdgeList = edgeList.Count;
                    edgeList.Add(tempedges[j]);
                    newTriangulo.Edges[j] = tempedges[j];
                    tempedges[j].NotifyVerticesAboutEdge();
                }
                else
                {
                    edx.TrianguloPertencente.Add(newTriangulo);
                    newTriangulo.Edges[j] = edx;
                }
            }
            return newTriangulo;
        }

        Vector3 PointOnParametricSurface(float u, float v)
        {
            float w = 1 - v - u;
            return b300 * w * w * w + b030 * u * u * u + b003 * v * v * v + b210 * 3 * w * w * u + b120 * 3 * w * u * u + b201 * 3 * w * w * v +
                b021 * 3 * u * u * v + b102 * 3 * w * v * v + b012 * 3 * u * v * v + b111 * 6 * u * v * w;
        }
        Vector3 NormOnParametricSurface(float u, float v)
        {            
            float w = 1 - v - u;
            return n200 * w * w + n020 * u * u + n002 * v * v + n110 * w * u + n011 * u * v + n101 * w * v;
        }

        void calculateControlPoint()
        {
            Vector3[] pos = new Vector3[3];
            Vector3[] norm = new Vector3[3];

            for (int i = 0; i < 3; i++)
            {
                pos[i] = tri.Vertices[i].VertexInfo.Position;
                norm[i] = tri.Vertices[i].VertexInfo.Normal;
            }

            b300 = pos[0];
            b030 = pos[1];
            b003 = pos[2];
            b210 = (2 * pos[0] + pos[1] - getW(0, 1) * norm[0]) / 3f;
            b120 = (2 * pos[1] + pos[0] - getW(1, 0) * norm[1]) / 3f;
            b021 = (2 * pos[1] + pos[2] - getW(1, 2) * norm[1]) / 3f;
            b012 = (2 * pos[2] + pos[1] - getW(2, 1) * norm[2]) / 3f;
            b102 = (2 * pos[2] + pos[0] - getW(2, 0) * norm[2]) / 3f;
            b201 = (2 * pos[0] + pos[2] - getW(0, 2) * norm[0]) / 3f;

            Vector3 E = b210 + b120 + b021 + b012 + b102 + b201;
            E /= 4.0f;

            Vector3 V = b300 + b030 + b003;
            V /= 6;
            
            b111 = (E - V);

            n200 = norm[0];            
            n020 = norm[1];            
            n002 = norm[2];            

            //n110 = norm[0] + norm[1] - getV(0, 1) * (pos[0] - pos[1]);
            //n110.Normalize();

            //n011 = norm[1] + norm[2] - getV(1, 2) * (pos[1] - pos[2]);
            //n011.Normalize();

            //n101 = norm[2] + norm[0] - getV(2, 0) * (pos[2] - pos[0]);
            //n101.Normalize();

            n110 = norm[0] + norm[1] - getV(0, 1) * (pos[1] - pos[0]);
            n110.Normalize();

            n011 = norm[1] + norm[2] - getV(1, 2) * (pos[2] - pos[1]);
            n011.Normalize();

            n101 = norm[2] + norm[0] - getV(2, 0) * (pos[0] - pos[2]);
            n101.Normalize();

        }

        float getV(int i, int j)
        {
            return 2 * (Vector3.Dot((tri.Vertices[j].VertexInfo.Position - tri.Vertices[i].VertexInfo.Position), (tri.Vertices[i].VertexInfo.Normal + tri.Vertices[j].VertexInfo.Normal)) / (Vector3.Dot((tri.Vertices[j].VertexInfo.Position - tri.Vertices[i].VertexInfo.Position), (tri.Vertices[j].VertexInfo.Position - tri.Vertices[i].VertexInfo.Position))));
        }

        float getW(int i, int j)
        {
            return Vector3.Dot((tri.Vertices[j].VertexInfo.Position - tri.Vertices[i].VertexInfo.Position), tri.Vertices[i].VertexInfo.Normal);
        }

        Vector3 b300;
        Vector3 b003;
        Vector3 b030;
        Vector3 b102;
        Vector3 b201;
        Vector3 b012;
        Vector3 b021;
        Vector3 b120;
        Vector3 b210;
        Vector3 b111;

        Vector3 n200;
        Vector3 n020;
        Vector3 n002;
        Vector3 n110;
        Vector3 n011;
        Vector3 n101;

    }
}
